package com.scheduling.schedule.algorithm.impl;

import com.scheduling.machine.entity.Machine;
import com.scheduling.order.entity.Order;
import com.scheduling.product.entity.Operation;
import com.scheduling.schedule.algorithm.ScheduleAlgorithm;
import com.scheduling.schedule.dto.ScheduleResult;
import com.scheduling.schedule.entity.ProductionTask;
import com.scheduling.schedule.entity.ScheduleDetail;
import com.scheduling.schedule.entity.Tuple2;
import com.scheduling.schedule.entity.WorkOrder;
import com.scheduling.schedule.enums.ScheduleAlgorithmType;
import com.scheduling.schedule.service.MachineLoadService;
import com.scheduling.schedule.service.OperationScheduleService;
import com.scheduling.schedule.service.ScheduleDataService;
import com.scheduling.schedule.service.WorkOrderGenerationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 同种产品集中生产排产算法
 * 基于CombineAlgorithm的正确时间管理机制，实现批量生产策略
 * <p>
 * 核心特性：
 * 1. 同产品工单集中处理，减少换型时间
 * 2. 使用MultiValueMap管理机器空闲时间段
 * 3. 实现正确的工序依赖拓扑排序
 * 4. 批量效率提升策略
 * <p>
 * 完整流程：
 * 1. 输入：订单列表
 * 2. 按产品分组排序（数量大的产品优先）
 * 3. 订单 → 工单生成（批量策略）
 * 4. 工单 → 按产品批量生产任务（基于机器空闲时间表）
 * 5. 输出：完整排产结果
 *
 * @author 开发团队
 */
@Slf4j
@Component("PRODUCT_BATCH")
@RequiredArgsConstructor
public class ProductBatchAlgorithm implements ScheduleAlgorithm {

    private final WorkOrderGenerationService workOrderGenerationService;
    private final MachineLoadService machineLoadService;
    private final ScheduleDataService scheduleDataService;
    private final OperationScheduleService operationScheduleService;

    /**
     * 换型时间（分钟）- 不同产品间切换的设备调整时间
     */
    private static final int CHANGEOVER_TIME = 30;

    /**
     * 批量生产效率提升系数（相比单独生产可节省的时间比例）
     */
    private static final double BATCH_EFFICIENCY_FACTOR = 0.92;

    @Override
    public ScheduleResult schedule(List<Order> orders, LocalDateTime scheduleStartTime) {
        log.info("开始执行同种产品集中生产排产算法，订单数量: {}", orders.size());

        long startTime = System.currentTimeMillis();
        ScheduleResult result = new ScheduleResult();

        try {
            if (orders.isEmpty()) {
                throw new IllegalArgumentException("订单列表不能为空");
            }

            // ========== 第一阶段：订单 → 产品批量工单生成 ==========
            log.info("第一阶段：产品批量排序与工单生成");
            List<Order> batchSortedOrders = sortOrdersByBatchStrategy(orders);
            List<WorkOrder> workOrders = generateWorkOrders(batchSortedOrders, scheduleStartTime);

            // ========== 第二阶段：工单 → 批量生产任务 ==========
            log.info("第二阶段：批量生产任务生成");
            List<ProductionTask> productionTasks = generateProductionTasks(workOrders, scheduleStartTime);

            // ========== 第三阶段：生成排产详情 ==========
            List<ScheduleDetail> scheduleDetails = generateScheduleDetails(productionTasks);

            // ========== 计算统计信息 ==========
            LocalDateTime scheduleEndTime = calculateScheduleEndTime(productionTasks);
            BigDecimal totalUtilization = scheduleDataService.calculateTotalUtilization(productionTasks,
                    scheduleStartTime, scheduleEndTime);
            // 分析批量生产效果
            analyzeBatchEffectiveness(productionTasks);
            // ========== 封装结果 ==========
            result.setSuccess(true);
            result.setScheduleStartTime(scheduleStartTime);
            result.setScheduleEndTime(scheduleEndTime);
            result.setTotalUtilization(totalUtilization);
            result.setGeneratedWorkOrders(workOrders);
            result.setProductionTasks(productionTasks);
            result.setScheduleDetails(scheduleDetails);
            result.setAlgorithmType(getAlgorithmType());
            result.setAlgorithmDescription(getDescription());
            result.setTotalWorkOrders(workOrders.size());
            result.setTotalTasks(productionTasks.size());
            result.setTotalMachines(calculateMachineCount(productionTasks));
            result.setExecutionTimeMs(System.currentTimeMillis() - startTime);

            log.info("批量生产排产算法执行完成，工单: {}, 任务: {}, 设备利用率: {}%, 执行耗时: {}ms",
                    workOrders.size(), productionTasks.size(), totalUtilization, result.getExecutionTimeMs());

        } catch (Exception e) {
            log.error("批量生产排产算法执行失败", e);
            result.setSuccess(false);
            result.setErrorMessage("算法执行失败: " + e.getMessage());
        }

        return result;
    }

    /**
     * 批量生产排序策略
     * 简化策略：按截止时间和创建时间排序，批量效果由工单生成时实现
     */
    private List<Order> sortOrdersByBatchStrategy(List<Order> orders) {
        log.debug("执行批量生产排序策略");

        // 简化排序：按截止时间优先，然后按创建时间
        orders.sort((o1, o2) -> {
            // 1. 按截止时间排序（早的优先）
            if (o1.getDueDate() != null && o2.getDueDate() != null) {
                int dueDateCompare = o1.getDueDate().compareTo(o2.getDueDate());
                if (dueDateCompare != 0) return dueDateCompare;
            }

            // 2. 按创建时间排序
            return o1.getCreateTime().compareTo(o2.getCreateTime());
        });

        log.info("批量生产排序完成，订单总数: {}", orders.size());
        return orders;
    }

    @Override
    public List<WorkOrder> generateWorkOrders(List<Order> orders, LocalDateTime scheduleStartTime) {
        return workOrderGenerationService.generateBatchWorkOrders(orders, scheduleStartTime);
    }

    @Override
    public List<ProductionTask> generateProductionTasks(List<WorkOrder> workOrders, LocalDateTime scheduleStartTime) {
        List<ProductionTask> allTasks = new ArrayList<>();
        AtomicInteger taskSequence = new AtomicInteger(1);

        // 获取机器空闲时间表（使用CombineAlgorithm的高级时间管理）
        MultiValueMap<String, Tuple2<LocalDateTime, LocalDateTime>> machineAvailableTime =
                getMachineAvailableTime(scheduleStartTime);

        // 按产品分组处理批量生产
        Map<String, List<WorkOrder>> productGroups = workOrders.stream()
                .collect(Collectors.groupingBy(WorkOrder::getProductId));

        log.info("开始按产品批量生产，产品种类: {}", productGroups.size());

        boolean isFirstProduct = true;
        for (Map.Entry<String, List<WorkOrder>> entry : productGroups.entrySet()) {
            String productId = entry.getKey();
            List<WorkOrder> productWorkOrders = entry.getValue();

            log.info("开始处理产品 {} 的 {} 个工单", productId, productWorkOrders.size());

            // 如果不是第一个产品，为所有机器添加换型时间
            if (!isFirstProduct) {
                addChangeoverTimeToAllMachines(machineAvailableTime, CHANGEOVER_TIME);
                log.debug("为产品 {} 添加换型时间: {} 分钟", productId, CHANGEOVER_TIME);
            }

            // 批量处理该产品的所有工单
            for (WorkOrder workOrder : productWorkOrders) {
                try {
                    List<ProductionTask> orderTasks = createBatchTasksForWorkOrder(
                            workOrder, machineAvailableTime, taskSequence);
                    allTasks.addAll(orderTasks);

                    log.debug("产品 {} 工单 {} 生成了 {} 个任务",
                            productId, workOrder.getWorkOrderId(), orderTasks.size());
                } catch (Exception e) {
                    log.error("为产品 {} 工单 {} 生成批量任务失败: {}",
                            productId, workOrder.getWorkOrderId(), e.getMessage());
                }
            }

            isFirstProduct = false;
        }

        log.info("批量生产任务生成完成，总任务数: {}", allTasks.size());
        return allTasks;
    }

    /**
     * 为所有机器添加换型时间
     */
    private void addChangeoverTimeToAllMachines(
            MultiValueMap<String, Tuple2<LocalDateTime, LocalDateTime>> machineAvailableTime,
            int changeoverMinutes) {

        for (String machineId : machineAvailableTime.keySet()) {
            List<Tuple2<LocalDateTime, LocalDateTime>> timeSlots = machineAvailableTime.get(machineId);

            if (!timeSlots.isEmpty()) {
                // 获取最早的空闲时间段并延后换型时间
                timeSlots.sort(Comparator.comparing(Tuple2::getFirst));
                Tuple2<LocalDateTime, LocalDateTime> earliestSlot = timeSlots.get(0);

                LocalDateTime newStart = earliestSlot.getFirst().plusMinutes(changeoverMinutes);

                // 更新时间段
                timeSlots.remove(0);
                if (newStart.isBefore(earliestSlot.getSecond())) {
                    timeSlots.add(0, new Tuple2<>(newStart, earliestSlot.getSecond()));
                }

                // 重新排序
                timeSlots.sort(Comparator.comparing(Tuple2::getFirst));
            }
        }
    }

    /**
     * 依据工单生成批量生产任务（基于CombineAlgorithm的时间管理）
     */
    private List<ProductionTask> createBatchTasksForWorkOrder(
            WorkOrder workOrder,
            MultiValueMap<String, Tuple2<LocalDateTime, LocalDateTime>> machinesAvailableTime,
            AtomicInteger taskSequence
    ) {
        List<ProductionTask> tasks = new ArrayList<>();
        Integer productQuantity = workOrder.getQuantity();
        String productID = workOrder.getProductId();

        // 获取工序并排序
        List<Operation> operations = scheduleDataService.getOperationsByProductId(productID);
        operationScheduleService.sortOperations(operations);

        // 遍历工序生成批量任务
        LocalDateTime predecessorOpEndTime = LocalDateTime.MIN;
        for (Operation operation : operations) {
            // 处理前置工序约束
            if (operation.getPredecessorOps() != null) {
                String[] predecessorOps = operation.getPredecessorOps().split(",");
                for (String predecessorOpId : predecessorOps) {
                    Optional<ProductionTask> predTask = tasks.stream()
                            .filter(task -> Objects.equals(task.getOperationId(), predecessorOpId))
                            .findFirst();

                    if (predTask.isPresent()) {
                        LocalDateTime currentTime = predTask.get().getPlanEndTime();
                        if (currentTime.isAfter(predecessorOpEndTime)) {
                            predecessorOpEndTime = currentTime;
                        }
                    }
                }
            }

            // 找到需要的机器类型
            String machineTypeId = operation.getMachineTypeId();
            List<Machine> machines = scheduleDataService.getMachinesByType(machineTypeId);

            // 使用批量生产优化的机器选择策略
            String selectedMachineId = null;
            Tuple2<LocalDateTime, LocalDateTime> selectedInterval = null;

            for (Machine machine : machines) {
                // 计算批量任务的生产时间（应用批量效率因子）
                double machineCapacity = machine.getMaxCapacity().doubleValue();
                double hoursRequired = productQuantity / machineCapacity;
                double halfHourUnits = Math.ceil(hoursRequired * 2);
                double totalHours = halfHourUnits * 0.5 * BATCH_EFFICIENCY_FACTOR; // 应用批量效率
                Duration duration = Duration.ofMinutes((long) (totalHours * 60));

                String machineId = machine.getMachineId();
                List<Tuple2<LocalDateTime, LocalDateTime>> freeTimes = machinesAvailableTime.get(machineId);
                freeTimes.sort(Comparator.comparing(Tuple2::getFirst));

                // 找到最早能排产的时间段
                for (Tuple2<LocalDateTime, LocalDateTime> freeTime : freeTimes) {
                    LocalDateTime freeStart = freeTime.getFirst();
                    LocalDateTime freeEnd = freeTime.getSecond();

                    if (freeEnd.isBefore(predecessorOpEndTime)) continue;

                    if (freeStart.isBefore(predecessorOpEndTime)) {
                        freeStart = predecessorOpEndTime;
                    }

                    Duration freeDuration;
                    if (freeEnd.equals(LocalDateTime.MAX)) {
                        freeDuration = Duration.ofSeconds(Long.MAX_VALUE);
                    } else {
                        freeDuration = Duration.between(freeStart, freeEnd);
                    }

                    if (freeDuration.compareTo(duration) >= 0) {
                        LocalDateTime taskEnd = freeStart.plusMinutes(duration.toMinutes());
                        Tuple2<LocalDateTime, LocalDateTime> allocateInterval = new Tuple2<>(
                                freeStart, taskEnd);

                        // 批量生产策略：选择最早完成的时间
                        if (selectedMachineId == null) {
                            selectedMachineId = machineId;
                            selectedInterval = allocateInterval;
                        } else if (taskEnd.isBefore(selectedInterval.getSecond())) {
                            selectedMachineId = machineId;
                            selectedInterval = allocateInterval;
                        } else if (taskEnd.isEqual(selectedInterval.getSecond())) {
                            if (freeStart.isAfter(allocateInterval.getFirst())) {
                                selectedMachineId = machineId;
                                selectedInterval = allocateInterval;
                            }
                        }
                        break;
                    }
                }
            }

            // 生成批量任务
            if (selectedInterval != null) {
                ProductionTask task = new ProductionTask();
                String timeStamp = String.valueOf(System.currentTimeMillis()).substring(7);
                Duration productDuration = Duration.between(selectedInterval.getFirst(), selectedInterval.getSecond());
                LocalDateTime taskStart = selectedInterval.getFirst();
                LocalDateTime taskEnd = selectedInterval.getSecond();

                task.setTaskId("BATCH_" + timeStamp + String.format("%03d", taskSequence.getAndIncrement()));
                task.setWorkOrderId(workOrder.getWorkOrderId());
                task.setOperationId(operation.getOperationId());
                task.setMachineId(selectedMachineId);
                task.setQuantity(productQuantity);
                task.setStatus("已分配");
                task.setStandardTime((int) productDuration.toMinutes());
                task.setPlannedTime((int) productDuration.toMinutes());
                task.setPlanStartTime(taskStart);
                task.setPlanEndTime(taskEnd);
                task.setCreateTime(LocalDateTime.now());
                task.setUpdateTime(LocalDateTime.now());

                tasks.add(task);

                log.info("批量任务分配 - 产品: {} 工单: {} 工序: {} 机器: {} 时间: {} - {} (效率: {}%)",
                        productID, task.getWorkOrderId(), task.getOperationId(), task.getMachineId(),
                        selectedInterval.getFirst(), selectedInterval.getSecond(),
                        Math.round(BATCH_EFFICIENCY_FACTOR * 100));

                // 更新机器空闲时间表
                updateMachineAvailableTime(machinesAvailableTime, selectedMachineId, selectedInterval);
            }
        }

        return tasks;
    }

    /**
     * 更新机器空闲时间表（基于CombineAlgorithm的逻辑）
     */
    // TODO: 更新机器空闲时间也可以抽出作为服务，各个算法中该方法都一样
    private void updateMachineAvailableTime(
            MultiValueMap<String, Tuple2<LocalDateTime, LocalDateTime>> machinesAvailableTime,
            String machineId,
            Tuple2<LocalDateTime, LocalDateTime> usedInterval) {

        LocalDateTime taskStart = usedInterval.getFirst();
        LocalDateTime taskEnd = usedInterval.getSecond();

        List<Tuple2<LocalDateTime, LocalDateTime>> machineFreeTime = machinesAvailableTime.get(machineId);
        Iterator<Tuple2<LocalDateTime, LocalDateTime>> iterator = machineFreeTime.iterator();
        List<Tuple2<LocalDateTime, LocalDateTime>> newSlots = new ArrayList<>();

        while (iterator.hasNext()) {
            Tuple2<LocalDateTime, LocalDateTime> slot = iterator.next();
            LocalDateTime slotStart = slot.getFirst();
            LocalDateTime slotEnd = slot.getSecond();

            if (!taskStart.isBefore(slotStart) && !taskEnd.isAfter(slotEnd)) {
                iterator.remove();

                if (slotStart.isBefore(taskStart)) {
                    newSlots.add(new Tuple2<>(slotStart, taskStart));
                }

                if (taskEnd.isBefore(slotEnd)) {
                    newSlots.add(new Tuple2<>(taskEnd, slotEnd));
                }

                break;
            }
        }

        machineFreeTime.addAll(newSlots);
        machineFreeTime.sort(Comparator.comparing(Tuple2::getFirst));
    }

    /**
     * 获取机器空闲时间表（使用CombineAlgorithm的高级时间管理）
     */
    // TODO: 获取机器空闲时间表也可以抽出作为服务，各个算法中该方法都一样
    private MultiValueMap<String, Tuple2<LocalDateTime, LocalDateTime>> getMachineAvailableTime(LocalDateTime scheduleStartTime) {
        return machineLoadService.getCurrentMachineAvailablePeriod(scheduleStartTime);
    }

    private List<ScheduleDetail> generateScheduleDetails(List<ProductionTask> productionTasks) {
        List<ScheduleDetail> details = new ArrayList<>();
        AtomicInteger sequence = new AtomicInteger(1);
        String timeStamp = String.valueOf(System.currentTimeMillis()).substring(7);

        for (ProductionTask task : productionTasks) {
            ScheduleDetail detail = new ScheduleDetail();
            detail.setDetailId("BATCH_DETAIL_" + timeStamp + String.format("%03d", sequence.getAndIncrement()));
            detail.setTaskId(task.getTaskId());
            detail.setMachineId(task.getMachineId());
            detail.setScheduledStartTime(task.getPlanStartTime());
            detail.setScheduledEndTime(task.getPlanEndTime());
            detail.setSequenceNo(sequence.get());
            detail.setCreateTime(LocalDateTime.now());

            details.add(detail);
        }

        return details;
    }

    private LocalDateTime calculateScheduleEndTime(List<ProductionTask> productionTasks) {
        return productionTasks.stream()
                .map(ProductionTask::getPlanEndTime)
                .filter(Objects::nonNull)
                .max(LocalDateTime::compareTo)
                .orElse(LocalDateTime.now());
    }


    /**
     * 分析批量生产效果
     */
    private void analyzeBatchEffectiveness(List<ProductionTask> productionTasks) {
        // 简化分析：统计任务数量和批量效率
        int totalTasks = productionTasks.size();
        int totalSavedTime = totalTasks * (int) ((1 - BATCH_EFFICIENCY_FACTOR) * 60); // 估算节省时间

        // 按机器分组统计
        Map<String, Long> machineTaskCounts = productionTasks.stream()
                .collect(Collectors.groupingBy(
                        task -> task.getMachineId() != null ? task.getMachineId() : "UNKNOWN",
                        Collectors.counting()
                ));

        log.info("=== 批量生产效果分析 ===");
        log.info("总任务数: {}", totalTasks);
        log.info("涉及机器数: {}", machineTaskCounts.size());
        machineTaskCounts.forEach((machineId, count) ->
                log.info("机器 {} 任务数: {}", machineId, count));
        log.info("批量效率系数: {}%", Math.round(BATCH_EFFICIENCY_FACTOR * 100));
        log.info("估算节省时间: {} 分钟", totalSavedTime);
        log.info("========================");
    }

    private int calculateMachineCount(List<ProductionTask> productionTasks) {
        return (int) productionTasks.stream()
                .map(ProductionTask::getMachineId)
                .filter(Objects::nonNull)
                .distinct()
                .count();
    }

    @Override
    public String getAlgorithmType() {
        return ScheduleAlgorithmType.PRODUCT_BATCH.getCode();
    }

    @Override
    public String getDescription() {
        return ScheduleAlgorithmType.PRODUCT_BATCH.getDescription();
    }
} 